<template>
  <div id="comment-warpper" class="comment-box" :class="{ mobile: isMobile }">
    <comment-topbar
      :total="comments.pagination?.total"
      :likes="likes"
      :article-id="articleId"
      :sort="state.sort"
      :fetching="isFetching"
      @sort="getSortComments"
    />

    <comment-list :fetching="isFetching" :comments="comments.data" @reply="replyComment" />

    <comment-pagination
      :fetching="isFetching"
      :pagination="comments.pagination"
      :sort="state.sort"
      @page="getPageComments"
    />

    <comment-publisher
      :disabled="isPostingComment || isFetching"
      :cached="userState.cached"
      :editing="userState.editing"
      :reply-pid="state.replyPid"
      v-model:profile="profileState"
      @cancel-reply="resetCommentReply"
      @edit-profile="editUserProfile"
      @save-profile="syncUserProfileToStorage"
      @clear-profile="clearUserProfile"
    >
      <template #pen>
        <comment-pen
          v-model="penState.content"
          :preview="penState.preview"
          :disabled="isPostingComment || isFetching"
          :posting="isPostingComment"
          @input-ready="handleMarkdownInputReady"
          @toggle-preview="togglePenPreview"
          @submit="submitComment"
        />
      </template>
    </comment-publisher>
  </div>
</template>

<script>
import { computed, defineComponent, onMounted, reactive, ref, watch, onBeforeMount, onUnmounted, watchEffect } from "vue";
import { getJSON, setJSON, remove } from "@/services/storage";
import CommentTopbar from "./topbar.vue";
import CommentList from "./list/index.vue";
import CommentPagination from './pagination.vue'
import CommentPublisher from './publisher.vue'
import CommentPen from './pen.vue'
import { useEnhandle } from "@/enhandle";
import { scrollToElementAnchor } from "@/utils/scroller"
import { email as emailRegex, url as urlRegex } from '@/utils/regex'

export default defineComponent({
  name: "Comment",
  components: {
    CommentTopbar,
    CommentList,
    CommentPagination,
    CommentPublisher,
    CommentPen
  },
  props: {
    fetching: {
      type: Boolean,
      default: false
    },
    articleId: {
      type: Number,
      required: true,
    },
    likes: {
      type: Number,
      default: 0,
    },
  },
  setup(props) {
    const { store, isMobile } = useEnhandle();
    const markdownInputElement = ref(null);

    const comments = computed(() => store.state.comment.comments);
    const isFetchingComment = computed(() => store.state.comment.fetching);
    const isPostingComment = computed(() => store.state.comment.posting);

    const isFetching = computed(() => {
      return props.fetching || isFetchingComment.value
    })

    const state = reactive({
      sort: -1,
      replyPid: 0
    });

    const userState = reactive({
      cached: false,
      editing: false
    })

    const baseProfile = {
      name: '',
      email: '',
      site: ''
    }

    const profileState = ref(baseProfile)

    const penState = reactive({
      content: '',
      preview: false
    })

    const syncUserStorageToState = () => {
      const user = getJSON('user')
      if (user) {
        userState.cached = true
        userState.editing = false
        profileState.value = user
      }
    }

    const syncUserProfileToStorage = () => {
      userState.cached = true
      userState.editing = false
      setJSON('user', profileState.value)
    }

    const clearUserProfile = () => {
      userState.cached = false
      userState.editing = false
      profileState.value = { ...baseProfile }
      remove('user')
    }

    const editUserProfile = () => {
      userState.editing = true
    }

    const handleMarkdownInputReady = (markdownElement) => {
      markdownInputElement.value = markdownElement
    }

    const resetCommentReply = () => state.replyPid = 0

    const clearPenContent = () => penState.content = ''

    const togglePenPreview = () => penState.preview = !penState.preview

    const replyComment = (commentId) => {
      state.replyPid = commentId
      scrollToElementAnchor("comment-publisher", 300)
      // if (markdownInputElement.value) {
      //   focusPosition(markdownInputElement.value)
      // }
    }

    const fetchCommentList = (params = {}) => {
      // 每次重新获取数据都需要回到顶部评论框
      scrollToElementAnchor("comment-warpper", -73)
      store.dispatch('comment/fetchList', {
        ...params,
        sort: state.sort,
        article_id: props.articleId
      })
    }

    // 获取排序评论
    const getSortComments = (sort) => {
      if (state.sort !== sort) {
        state.sort = sort
        fetchCommentList()
      }
    }

    const getPageComments = (page) => {
      fetchCommentList({ page })
    }

    const submitComment = async () => {
      const profile = profileState.value
      if (!profile.name) {
        return alert("姓名?")
      }
      if (!profile.email) {
        return alert("邮箱?")
      }
      if (!emailRegex.test(profile.email)) {
        return alert("邮箱格式错误~");
      }
      if (profile.site && !urlRegex.test(profile.site)) {
        return alert("网站格式错误~");
      }
      if (!penState.content || !penState.content.trim()) {
        return alert("评论内容?")
      }

      return store.dispatch("comment/fetchPostComment", {
        parent: state.replyPid,
        article_id: props.articleId,
        content: penState.content,
        agent: navigator.userAgent,
        author: profile,
      }).then(resultData => {
        penState.preview = false
        userState.cached = true
        userState.editing = false

        // 初始化评论回复
        resetCommentReply()
        clearPenContent()

        // 设置用户
        syncUserProfileToStorage()
      }).catch(error => {
        console.warn("评论发布失败", error)
      })
    }

    watch(
      () => isFetching.value,
      isFetching => {
        if (isFetching) {
          resetCommentReply()
        }
      }
    )

    onMounted(() => {
      syncUserStorageToState()
    });

    onBeforeMount(() => {
      resetCommentReply()
    })

    onUnmounted(() => {
      store.commit("comment/clearListData")
    })

    return {
      isMobile,
      isFetching,
      isPostingComment,
      state,
      userState,
      profileState,
      penState,
      editUserProfile,
      syncUserProfileToStorage,
      clearUserProfile,
      togglePenPreview,
      comments,
      replyComment,
      submitComment,
      resetCommentReply,
      getSortComments,
      getPageComments,
      handleMarkdownInputReady
    };
  },
});
</script>

<style lang="scss">
.comment-box {
  padding: $gap;
  @include common-bg-module();
  @include radius-box($lg-radius);
}
</style>
